package com.bckj.fastboot.core.page.aspect;

import com.bckj.fastboot.core.lang.model.PageResult;
import com.bckj.fastboot.core.page.annotation.PageQuery;
import com.github.pagehelper.PageHelper;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.math.BigInteger;
import java.util.Arrays;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * PageQueryAspect
 */
@Slf4j
@Aspect
public class PageQueryAspect {

	private final static String ASC = "ASC";
	private final static String DESC = "DESC";

	@Around("@annotation(pageQuery)")
	public Object around(ProceedingJoinPoint joinPoint, PageQuery pageQuery) throws Throwable {
		HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
		int pageNum = getIntParameter(request, "pageNum", 1);
		int pageSize = getIntParameter(request, "pageSize", 10);
		int pageTotal = getIntParameter(request, "pageTotal", 0);
		String orderByParam = request.getParameter("orderBy");
		String orderBy = null;
		if (orderByParam != null && !orderByParam.isEmpty()) {
			orderByParam = this.camelToSnakeCase(orderByParam);
			String isAsc = request.getParameter("isAsc");
			if (StringUtils.isBlank(isAsc)) {
				isAsc = "1";
			}
			isAsc = ("1".equals(isAsc) ? ASC : DESC);
			orderBy = validateOrderBy(orderByParam, pageQuery.allowedSortFields(),isAsc);
		} else {
			String defaultOrderBy = pageQuery.defaultOrderBy();
			if (defaultOrderBy != null && !defaultOrderBy.isEmpty()) {
				orderBy = defaultOrderBy;
			}
		}
		PageHelper.startPage(pageNum, pageSize, pageTotal == 0);
		if (orderBy != null && !orderBy.isEmpty()) {
			PageHelper.orderBy(orderBy);
		}

		try {
			Object r = joinPoint.proceed();
			if (pageTotal > 0 && r instanceof PageResult<?> pageResult) {
				pageResult.setTotal(new BigInteger(String.valueOf(pageTotal)));
			}
			return r;
		} finally {
			PageHelper.clearPage();
		}
	}

	private int getIntParameter(HttpServletRequest request, String paramName, int defaultValue) {
		String paramValue = request.getParameter(paramName);
		if (paramValue != null) {
			try {
				return Integer.parseInt(paramValue);
			} catch (NumberFormatException e) {
				// Ignore
			}
		}
		return defaultValue;
	}

	private String validateOrderBy(String orderByParam, String[] allowedSortFields, String defaultSortDirection) {
		if (orderByParam != null && !orderByParam.isEmpty() && allowedSortFields.length > 0) {
			Set<String> allowedFieldsSet = Arrays.stream(allowedSortFields).collect(Collectors.toSet());

			String[] orderByParts = orderByParam.split("\\s+");
			String field = orderByParts[0];
			String direction = orderByParts.length > 1 ? orderByParts[1].toUpperCase() : defaultSortDirection;

			if (allowedFieldsSet.contains(field) && (ASC.equals(direction) || DESC.equals(direction))) {
				return field + " " + direction;
			}
		}
		return null;
	}

	public String camelToSnakeCase(String str) {
		String regex = "(?<=[a-z])[A-Z]";
		str = str.replaceAll(regex, "_$0");
		return str.toLowerCase();
	}
}
